﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using PMC.Properties;

namespace PMC
{
    public static class NativeMethods
    {
        #region enums

        public enum ErrorFlags
        {
            ERROR_INSUFFICIENT_BUFFER = 122,
            ERROR_INVALID_PARAMETER = 87,
            ERROR_NO_MORE_ITEMS = 259
        }

        public enum InternetFlags
        {
            INTERNET_COOKIE_HTTPONLY = 8192, //Requires IE 8 or higher
            INTERNET_COOKIE_THIRD_PARTY = 131072,
            INTERNET_FLAG_RESTRICTED_ZONE = 16
        }

        /// <summary>
        /// INTERNETFEATURELIST Enum representing navigating 'click' sound
        /// </summary>
        private const int FEATURE_DISABLE_NAVIGATION_SOUNDS = 21;
        /// <summary>
        /// DWORD representing the current process
        /// </summary>
        private const int SET_FEATURE_ON_PROCESS = 0x00000002;

        #endregion

        #region DLL Imports

        [SuppressUnmanagedCodeSecurity, SecurityCritical, DllImport("wininet.dll", EntryPoint = "InternetGetCookieExW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
        internal static extern bool InternetGetCookieEx([In] string Url, [In] string cookieName, [Out] StringBuilder cookieData, [In, Out] ref uint pchCookieData, uint flags, IntPtr reserved);

        [SuppressUnmanagedCodeSecurity, SecurityCritical, DllImport("wininet.dll", EntryPoint = "InternetSetCookieExW", CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)]
        internal static extern bool InternetSetCookieEx([In] string Url, [In] string cookieName, [In] StringBuilder cookieData, uint flags, IntPtr reserved);
        
        [DllImport("urlmon.dll"), PreserveSig]
        [return:MarshalAs(UnmanagedType.Error)]
        internal static extern int CoInternetSetFeatureEnabled(int FeatureEntry, [MarshalAs(UnmanagedType.U4)] int dwFlags, bool fEnable);

        [DllImport("winmm.dll")]
        internal static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume);

        [DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern bool InternetSetCookie(
            string lpszUrlName,
            string lpszCookieName,
            string lpszCookieData
        );

        #endregion

        private static bool isSoundEnabled = Settings.Default.SoundEnabled;
        /// <summary>
        /// Disables/Enables sound of browser
        /// <br/>
        /// Credit: http://social.msdn.microsoft.com/Forums/is/csharpgeneral/thread/1bf64853-2800-45d5-9c6b-206e2d92d58e
        /// </summary>
        public static bool IsSoundEnabled
        {
            get { return isSoundEnabled; }
            set
            {
                isSoundEnabled = value;
                SetVolume(value ? 100 : 0); //Disables sound of current process
                CoInternetSetFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS, SET_FEATURE_ON_PROCESS, !value); //Disables click sound
            }
        }

        public static void SetVolume(int percentage)
        {
            if (percentage < 0)
                percentage = 0;
            if (percentage > 100)
                percentage = 100;

            int NewVolume = ((UInt16.MaxValue / 10) * percentage);
            uint NewVolumeAllChannels = (((uint)NewVolume & 0x0000ffff) | ((uint)NewVolume << 16));
            waveOutSetVolume(IntPtr.Zero, NewVolumeAllChannels); 
        }

        /// <summary>
        /// Gets cookie from IE
        /// </summary>
        /// <param name="uri">Url of cookie</param>
        /// <param name="throwIfNoCookie">Boolean wether to throw Exceptions if none found</param>
        /// <returns>List of Cookies</returns>
        [SecurityCritical]
        [Obsolete]
        public static CookieCollection GetCookieInternal(Uri uri)
        {
            var returnCollection = new CookieCollection();
            var str = GetCookieInternal(uri, false);
            str.Replace(";", ",");
            
            foreach (var cookies in str.Split(new[] { "; "}, StringSplitOptions.RemoveEmptyEntries))
            {
                var split = cookies.Trim().Split(new[] {"="}, StringSplitOptions.RemoveEmptyEntries);
                if (split.Length == 2)
                    returnCollection.Add(new Cookie(split[0], split[1], uri.LocalPath, uri.Host));
            }

            //foreach (Cookie cookie in returnCollection)
            //{
            //    System.Windows.Forms.MessageBox.Show(cookie.ToString());
            //}

            return returnCollection;
        }

        [Obsolete]
        public static string GetCookieInternal(Uri uri, bool throwIfNoCookie)
        {
            uint pchCookieData = 0;

            string url = UriToString(uri);
            uint flag = (uint)InternetFlags.INTERNET_COOKIE_HTTPONLY;

            //Gets the size of the string builder
            if (InternetGetCookieEx(url, null, null, ref pchCookieData, flag, IntPtr.Zero))
            {
                pchCookieData++;

                StringBuilder cookieData = new StringBuilder((int)pchCookieData);
                string tempData = String.Empty;

                //Read the cookie
                if (InternetGetCookieEx(url, null, cookieData, ref pchCookieData, flag, IntPtr.Zero))
                {
                    DemandWebPermission(uri);

                    tempData = cookieData.ToString();
                }
                return tempData;
            }

            int lastErrorCode = Marshal.GetLastWin32Error();

            if (throwIfNoCookie/* || (lastErrorCode != (int)ErrorFlags.ERROR_NO_MORE_ITEMS)*/)
            {
                throw new Win32Exception(lastErrorCode);
            }

            return String.Empty;
        }

        private static void DemandWebPermission(Uri uri)
        {
            string uriString = UriToString(uri);

            if (uri.IsFile)
            {
                string localPath = uri.LocalPath;

                new FileIOPermission(FileIOPermissionAccess.Read, localPath).Demand();
            }
            else
            {
                new WebPermission(NetworkAccess.Connect, uriString).Demand();
            }
        }

        private static string UriToString(Uri uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            UriComponents components = (uri.IsAbsoluteUri ? UriComponents.AbsoluteUri : UriComponents.SerializationInfoString);

            return new StringBuilder(uri.GetComponents(components, UriFormat.SafeUnescaped), 2083).ToString();
        }

        //http://www.codewrecks.com/blog/index.php/2011/06/29/pass-cookie-from-web-request-to-webbrowser/
        [Obsolete]
        public static bool SetCookieForBrowserControl(CookieContainer cc, Uri uri)
        {
            //Todo what would be the difference between the host or the whole uri for storing cookies?
            //String hostName = uri.Scheme + Uri.SchemeDelimiter + uri.Host;

            //Uri hostUri = new Uri(hostName);
            return SetCookiesInternal(cc.GetCookies(uri), uri);
        }

        [Obsolete]
        public static bool SetCookiesInternal(CookieCollection c, Uri uri)
        {
            String hostName = uri.Scheme + Uri.SchemeDelimiter + uri.Host;
            bool succes = true;
            foreach (Cookie cookie in c)
            {

                if (!InternetSetCookieEx(hostName, cookie.Name, new StringBuilder(cookie.Value), 0, IntPtr.Zero))
                    succes = false;
            }
            return succes;
        }
    }
}
